package com.sanmubird.mywebframework.indicator.domain.executor;

import com.alibaba.fastjson.JSONObject;
import com.ql.util.express.DefaultContext;
import com.ql.util.express.ExpressRunner;
import com.ql.util.express.IExpressContext;
import com.sanmubird.mywebframework.indicator.domain.model.Indicator;
import com.sanmubird.mywebframework.indicator.domain.model.IndicatorExecutor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.context.ApplicationContext;

import java.util.ArrayList;
import java.util.Map;


/**
 * @author sam
 */
@Slf4j
public class JavaExecutor implements IndicatorExecutor {

    private static final ExpressRunner RUNNER = new ExpressRunner();
    private final ApplicationContext beanProvider;

    /** 类名 */
    private static final String BEAN_NAME = "beanName";
    /** 方法名 */
    private static final String METHOD_NAME = "methodName";
    /** 固定参数 */
    private static final String FIXED_PARAMETER = "fixedParameter";

    public JavaExecutor(ApplicationContext beanProvider) {
        this.beanProvider = beanProvider;
    }

    private String buildScript(String beanName, String methodName, Map<String, Class> parameters) {
        StringBuilder script = new StringBuilder("return ");
        script.append(beanName).append(".").append(methodName).append("( ");
        parameters.forEach((name, type) -> script.append(name).append(","));
        script.deleteCharAt(script.length() - 1);
        script.append(")");
        return script.toString();
    }

    public void validate(Indicator indicator) {
        JSONObject params = indicator.getParams();
        if (params == null) {
            throw new IllegalArgumentException("请检查元素配置，缺少必要的配置信息!");
        }
        if (StringUtils.isBlank(params.getString(BEAN_NAME))) {
            throw new IllegalArgumentException("没有指定beanName!");
        }
        if (StringUtils.isBlank(params.getString(METHOD_NAME))) {
            throw new IllegalArgumentException("没有指定methodName");
        }
    }

    @Override
    public Object execute(Indicator indicator, JSONObject queryParams) {
        validate(indicator);
        Object object;
        JSONObject params = indicator.getParams();
        IExpressContext<String, Object> expressContext = new DefaultContext<>();
        String beanName = params.getString(BEAN_NAME);
        String methodName = params.getString(METHOD_NAME);
        // 方法固定参数，数值动态可配
        JSONObject fixedParameterMap = params.getJSONObject(FIXED_PARAMETER);

        Object bean = beanProvider.getBean(beanName);
        expressContext.put(beanName, bean);

        Map<String, Class> parameters = MethodParameterUtil.getMethodParameter(bean.getClass(), methodName);
        parameters.forEach((name, type) -> {
            if (MethodParameterUtil.isJavaClass((Class<?>) type)) {
                // 基础数据类型入参中取值 优先取固定参数中获取，固定参数中不存在再从入参获取
                if (fixedParameterMap != null && fixedParameterMap.get(name) != null) {
                    expressContext.put(name, fixedParameterMap.get(name));
                } else {
                    expressContext.put(name, queryParams.get(name));
                }
            } else {
                // 自己定义的类型 需要将JSONObject转对象
                expressContext.put(name, JSONObject.toJavaObject(queryParams, (Class<?>) type));
            }
        });

        try {
            object = RUNNER.execute(buildScript(beanName, methodName, parameters), expressContext, new ArrayList<>(), false, false);
        } catch (ClassCastException classCastException) {
            throw new ExecuteException("java代码执行的返回值必须是JSONObject!", classCastException);
        } catch (Exception e) {
            throw new ExecuteException("执行方法出现异常！", e);
        }
        return object;
    }


}
